
#include "RtBase.h"
#include "RtThread.h"
#include "RtThreadInterface.h"
#include "RtConditionVariable.h"

///////////////////////////////////////////////////////////////
// class CRtThread
///////////////////////////////////////////////////////////////

CRtThread::CRtThread()
	: m_Tid(0)
#ifdef RT_WIN32
	, m_Handle(RT_INVALID_HANDLE)
#else
	, m_Handle((RT_THREAD_HANDLE)RT_INVALID_HANDLE)
#endif // RT_WIN32
	, m_Type(CRtThreadManager::TT_UNKNOWN)
	, m_Flag(CRtThreadManager::TF_NONE)
	, m_stopped(FALSE)
	, m_pEvent4Start(NULL)
	, m_bRegistered(FALSE)
{
}

CRtThread::~CRtThread()
{
	RT_INFO_TRACE_THIS("CRtThread::~CRtThread m_bRegistered="<<m_bRegistered);
	if (m_bRegistered) {
		CRtThreadManager::Instance()->UnregisterThread(this);
		m_bRegistered = FALSE;
	}
}

RtResult CRtThread::Create(CRtThreadManager::TType aType, CRtThreadManager::TFlag aFlag)
{
	RT_INFO_TRACE_THIS("CRtThread::Create,"
		" aType=" << aType << 
		" aFlag=" << aFlag);

	RT_ASSERTE_RETURN(m_Type == CRtThreadManager::TT_UNKNOWN, RT_ERROR_ALREADY_INITIALIZED);
	RT_ASSERTE_RETURN(aType != CRtThreadManager::TT_UNKNOWN, RT_ERROR_INVALID_ARG);
	m_Type = aType;
	m_Flag = aFlag;
	if (m_Flag == CRtThreadManager::TF_NONE)
		m_Flag = CRtThreadManager::TF_JOINABLE;
	
	if (m_Type == CRtThreadManager::TT_MAIN) {
		// We have to assume the current thread is main thread.
		m_Tid = CRtThreadManager::GetThreadSelfId();
	}
	else {
		RT_ASSERTE(!m_pEvent4Start);
		m_pEvent4Start = new CRtEventThread();
		if (!m_pEvent4Start)
			return RT_ERROR_OUT_OF_MEMORY;

#ifdef RT_WIN32
		m_Handle = (HANDLE)::_beginthreadex(
			NULL,
			0,
			ThreadProc,
			this,
			0,
			(unsigned int *)(&m_Tid));
		if (m_Handle == 0) {
			RT_ERROR_TRACE("CRtThread::Create, _beginthreadex() failed! err=" << errno);
			return RT_ERROR_UNEXPECTED;
		}
#else // !RT_WIN32
		pthread_attr_t attr;
		int nRet;
		if ((nRet = ::pthread_attr_init(&attr)) != 0) {
			RT_ERROR_TRACE("CRtThread::Create, pthread_attr_init() failed! err=" << nRet);
			return RT_ERROR_UNEXPECTED;
		}

		int dstate = PTHREAD_CREATE_JOINABLE;
		if (RT_BIT_ENABLED(m_Flag, CRtThreadManager::TF_JOINABLE))
			dstate = PTHREAD_CREATE_JOINABLE;
		else if (RT_BIT_ENABLED(m_Flag, CRtThreadManager::TF_DETACHED))
			dstate = PTHREAD_CREATE_DETACHED;
		if ((nRet = ::pthread_attr_setdetachstate(&attr, dstate)) != 0) {
			RT_ERROR_TRACE("CRtThread::Create, pthread_attr_setdetachstate() failed! err=" << nRet);
			::pthread_attr_destroy(&attr);
			return RT_ERROR_UNEXPECTED;
		}

		if ((nRet = ::pthread_create(&m_Tid, &attr, ThreadProc, this)) != 0) {
			RT_ERROR_TRACE("CRtThread::Create, pthread_create() failed! err=" << nRet);
			::pthread_attr_destroy(&attr);
			return RT_ERROR_UNEXPECTED;
		}
		::pthread_attr_destroy(&attr);
		m_Handle = m_Tid;
#endif // RT_WIN32

		m_pEvent4Start->Wait();
		delete m_pEvent4Start;
		m_pEvent4Start = NULL;
	}

	RtResult rv = CRtThreadManager::Instance()->RegisterThread(this);
	if (RT_SUCCEEDED(rv))
		m_bRegistered = TRUE;
	else {
		Stop();
		Join();
	}
	return rv;
}

RtResult CRtThread::Destory(RtResult aReason)
{
	RT_INFO_TRACE_THIS("CRtThread::Destory, aReason=" << aReason);

	RtResult rv = RT_OK;
	if (m_bRegistered) {
		rv = CRtThreadManager::Instance()->UnregisterThread(this);
		m_bRegistered = FALSE;
	}
	RT_INFO_TRACE_THIS("CRtThread::Destory, after UnregisterThread rv="<<rv);
	if (RT_BIT_DISABLED(m_Flag, CRtThreadManager::TF_JOINABLE) && 
		++m_NeedDelete < 2)
	{
		return RT_OK;
	}
	
	delete this;
	RT_INFO_TRACE_THIS("CRtThread::Destory, after delete this");
	return RT_OK;
}

#ifdef RT_WIN32
unsigned WINAPI CRtThread::ThreadProc(void *aPara)
#else
void* CRtThread::ThreadProc(void *aPara)
#endif // RT_WIN32
{
	CRtThread *pThread = static_cast<CRtThread *>(aPara);
	RT_INFO_TRACE("CRtThread::ThreadProc, begin this="<<pThread);
	RT_ASSERTE_RETURN(pThread, NULL);

	pThread->OnThreadInit();
	if (pThread->m_Type != CRtThreadManager::TT_MAIN) {
		RT_ASSERTE(pThread->m_pEvent4Start);
		if (pThread->m_pEvent4Start)
			pThread->m_pEvent4Start->Signal();
	}

	pThread->OnThreadRun();

	if (RT_BIT_DISABLED(pThread->m_Flag, CRtThreadManager::TF_JOINABLE) && 
		++pThread->m_NeedDelete >= 2)
	{
		delete pThread;
	}

	RT_INFO_TRACE("CRtThread::ThreadProc, quit ... this="<<pThread);
	return NULL;
}

RtResult CRtThread::Stop(CRtTimeValue* aTimeout)
{
	RT_ASSERTE(!"CRtThread::Stop");
	return RT_ERROR_NOT_IMPLEMENTED;
}

RtResult CRtThread::Join(DWORD aMilliseconds)
{
//	RT_ASSERTE_RETURN(!CRtThreadManager::IsEqualCurrentThread(m_Tid), RT_ERROR_FAILURE);
	if (CRtThreadManager::IsEqualCurrentThread(m_Tid))
	{
		RT_WARNING_TRACE_THIS("CRtThread::Join, join in the same thread!");
		return RT_ERROR_FAILURE;
	}

#ifdef RT_WIN32
	if (m_Handle==RT_INVALID_HANDLE)
		return RT_OK;

	DWORD dwRet = ::WaitForSingleObject(m_Handle, aMilliseconds);
	RtResult rv = RT_OK;
	if (dwRet == WAIT_OBJECT_0)
	{
	}
	else if (dwRet == WAIT_TIMEOUT) {
		RT_WARNING_TRACE_THIS("CRtThread::Join, WaitForSingleObject() timeout! tid="<<m_Tid);
		TerminateThread(m_Handle, 119);
		rv = RT_ERROR_TIMEOUT;
	}
	else {
		RT_ERROR_TRACE_THIS("CRtThread::Join, WaitForSingleObject() failed! err=" << ::GetLastError()<<" tid="<<m_Tid);
		rv = RT_ERROR_FAILURE;
	}
	::CloseHandle(m_Handle);
	m_Handle = RT_INVALID_HANDLE;
	return rv;
#else
	void *pThreadReturn;
	int nRet = ::pthread_join(m_Tid, &pThreadReturn);
	if (nRet != 0) {
		RT_ERROR_TRACE_THIS("CRtThread::Join, pthread_join() failed! err=" << nRet<<" tid="<<m_Tid);
		return RT_ERROR_FAILURE;
	}
	else
		return RT_OK;
#endif // RT_WIN32
}

RT_THREAD_ID CRtThread::GetThreadId()
{
	return m_Tid;
}

CRtThreadManager::TType CRtThread::GetThreadType()
{
	return m_Type;
}

RT_THREAD_HANDLE CRtThread::GetThreadHandle()
{
	return m_Handle;
}

BOOL CRtThread::IsStopped() const
{
	return m_stopped;
}

IRtReactor* CRtThread::GetReactor()
{
	return NULL;
}

IRtEventQueue* CRtThread::GetEventQueue()
{
	return NULL;
}

IRtTimerQueue* CRtThread::GetTimerQueue()
{
	return NULL;
}

void CRtThread::OnThreadInit()
{
}

